package game.server.net;

import game.common.net.GameHandler;
import game.common.net.GamePacketHandler;
import game.common.util.Logger;
import game.common.world.Vector;
import game.server.world.GameObject;
import game.server.world.GameObjectVisitor;
import game.server.world.GameWorld;

import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import java.util.Map;

import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.IDataHandler;
import org.xsocket.connection.IDisconnectHandler;
import org.xsocket.connection.INonBlockingConnection;

public class InGameDataHandler extends GamePacketHandler implements IDataHandler, IDisconnectHandler{
	
	private Logger log = Logger.getLogger(getClass());
	
	private GameWorld world;
	
	private Map<String,INonBlockingConnection> connections;
	
	public InGameDataHandler(GameWorld world, Map<String,INonBlockingConnection> connections) {
		this.world = world;
		this.connections = connections;
	}

	@Override
	public boolean readPacket(int packetType, int packetLength, INonBlockingConnection nbc) throws IOException,BufferUnderflowException, ClosedChannelException, MaxReadSizeExceededException {
		GameSession sess = (GameSession) nbc.getAttachment();
		
		switch(packetType){
			case PTYPE_UPDATE_OBJECT_POSITION:
				return handleUpdatePosition(sess, nbc);
				
			default:
				log.debug("unkown packet type " + packetType);
				return false;
		}
	}
	
	private boolean handleUpdatePosition(GameSession sess, INonBlockingConnection nbc) throws IOException{
		GameObject obj = sess.getGameObject();
		String objectName = obj.getObjectName();
		
		Vector v = readVector(nbc);
		obj.moveTo(v);
		obj.setDir(v.getDir());
		obj.setSpeed(v.getSpeed());
		
		sendPositionToPlayersAround(objectName, v);
		
		return true;
	}
	
	private void sendPositionToPlayersAround(String objectName, Vector v){
		world.visitObjectsAtRange(v.getXf(), v.getYf(), 1000, new ObjectPositionUpdateSender(objectName, v, connections));
	}
	
	class ObjectPositionUpdateSender extends GameHandler implements GameObjectVisitor {
		private String objectName;
		private Vector position;
		
		private Map<String,INonBlockingConnection> connections;
		
		public ObjectPositionUpdateSender(String objectName, 
										Vector position, 
										Map<String,INonBlockingConnection> connections) {
			this.objectName = objectName;
			this.position = position;
			this.connections = connections;
		}
		
		@Override
		public void visit(GameObject obj) {
			String targetName = obj.getObjectName();
			
			INonBlockingConnection nbc = connections.get(targetName);
			
			try {
				// System.out.println("send " + objectName + " to " + targetName);
				synchronized(nbc){
					nbc.write(PTYPE_UPDATE_OBJECT_POSITION);
					nbc.write((short) (sizeOfPlayerName() + sizeOfVector()));
					writePlayerName(this.objectName, nbc);
					writeVector(this.position, nbc);
					nbc.flush();
				}
				
			} catch (BufferOverflowException e){
				e.printStackTrace();
			} catch (IOException e) {
				try {
					nbc.close();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	}

	@Override
	public boolean onDisconnect(INonBlockingConnection nbc) throws IOException {
		GameSession gs = (GameSession) nbc.getAttachment();
		GameObject obj = gs.getGameObject();
		
		System.out.println("disconnected " + obj.getObjectName());
		
		world.removeObject(obj);
		connections.remove(obj.getObjectName());
		
		for (INonBlockingConnection c : connections.values()){
			c.write(PTYPE_DESTROY_OBJECT);
			c.write(sizeOfPlayerName());
			writePlayerName(obj.getObjectName(), c);
			c.flush();
		}
		
		return true;
	}
}
